/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package uk.ac.aber.compsci.se3920.groupA.Tests;

import java.util.ArrayList;
import java.util.Calendar;
import uk.ac.aber.compsci.se3920.groupA.Tests.MockTypes.ActiveParticipantPreference;
import uk.ac.aber.compsci.se3920.groupA.Tests.MockTypes.ExclusionRange;
import uk.ac.aber.compsci.se3920.groupA.Tests.MockTypes.Meeting;
import uk.ac.aber.compsci.se3920.groupA.Tests.MockTypes.MeetingCandidate;
import uk.ac.aber.compsci.se3920.groupA.Tests.MockTypes.Room;
import uk.ac.aber.compsci.se3920.groupA.Tests.Utils.Debug;

/**
 *
 * @author rob
 */
public class MeetingTimeResolver {

    Meeting theMeeting;
    Config theConfig;
    private boolean workingDayPrinted = false;

    public MeetingTimeResolver(Meeting m, Config c) {
        theMeeting = m;
        theConfig = c;
        }

    public void addRoomsToMeetingCandidates(ArrayList<MeetingCandidate> candidates, ArrayList<Room> roomList) {
        ArrayList<Room> goodList = new ArrayList<Room>();

        for (Room r : roomList) {
            if (r.getCapacity() >= theMeeting.getMeetingAttendance()) {
                boolean roomValid = true;
                for (int i = 0; i < theMeeting.getMeetingEquipment().length; i++) {
                    roomValid &= r.hasEquipment(theMeeting.getMeetingEquipment()[i]);
                }
                if (roomValid) {
                    goodList.add(r);
                }
            }
        }

        for (MeetingCandidate mc : candidates) {
            for (Room validRoom : goodList) {
                mc.addRoom(validRoom);
            }
        }
    }

    public void reduceCandidatesToStartTimes(ArrayList<MeetingCandidate> candidates) {
        //bubble up the scores for start times.... no.
        //Create a new list of start times
        ArrayList<MeetingCandidate> startTimes = new ArrayList<MeetingCandidate>();
        //Loop through and find MC's where the next duration-1 are true and add them to the start times list
        for (int i = 0; i < candidates.size(); i++) {
            if (reductionHelperIsStartTime(candidates, i, theConfig.getNumCandidatesToMeetingDuration()) == true) {
                for (int j = i + 1; j < i + theConfig.getMeetingDuration(); j++) {
                    candidates.get(i).addActiveScore(candidates.get(j).getActiveScore());
                }
            } else {
                candidates.get(i).setValidity(false);
            }
        }
    }

    // Looks forward and reports on if meeting is a start time;
    public boolean reductionHelperIsStartTime(ArrayList<MeetingCandidate> candidates, int position, int duration) {
        if (position + duration > candidates.size() - 1) {
            return false;
        }
        for (int i = position; i < position + duration; i++) {
            if (candidates.get(i).isValidity() == false) {
                return false;
            }
        }
        return true;
    }

    public void trimByValidity(ArrayList<MeetingCandidate> candidates) {
        ArrayList<MeetingCandidate> falseList = new ArrayList<MeetingCandidate>();
        for (MeetingCandidate mc : candidates) {
            if (mc.isValidity() == false) {
                falseList.add(mc);
            }
        }

        for (MeetingCandidate mc : falseList) {
            candidates.remove(mc);
        }
    }

    public void scoreByActive(ArrayList<MeetingCandidate> candidates, ArrayList<ActiveParticipantPreference> appList) {
        for (MeetingCandidate mc : candidates) {
            for (ActiveParticipantPreference app : appList) {
                if (mc.getStart().getTime().compareTo(app.getStart().getTime()) >= 0) {
                    if (mc.getEnd().getTime().compareTo(app.getEnd().getTime()) <= 0) {
                        mc.addActiveScore(Config.ACTIVE_PREF_SCORE);
                    }
                }
            }
        }
    }

    public void cropByMeetingLength(ArrayList<MeetingCandidate> candidates, int meetingLength) {
        ArrayList<MeetingCandidate> possibleBlocks = new ArrayList<MeetingCandidate>();
        for (MeetingCandidate mc : candidates) {
            if (mc.isValidity()) {
                possibleBlocks.add(mc);
            } else {
                if (possibleBlocks.size() < meetingLength) {
                    for (MeetingCandidate bad : possibleBlocks) {
                        bad.setValidity(false);
                    }
                }
                possibleBlocks.clear();
            }
        }
    }

    public void cropByActiveExclusionSets(ArrayList<MeetingCandidate> theCandidates, ArrayList<ExclusionRange> theExclusions) {
        for (MeetingCandidate mc : theCandidates) {

            for (ExclusionRange ex : theExclusions) {
                if (!mc.isValidity()) {
                    continue;
                }
                //Check if MC should be excluded by directly matching an exclusions set or by 
                // startin in the middle of an exclusion set
                if (mc.getStart().getTime().after(ex.getStart().getTime()) || mc.getStart().getTime().compareTo(ex.getStart().getTime()) == 0) {
                    if (mc.getStart().getTime().before(ex.getEnd().getTime())) {

                        mc.setValidity(false);
                    }
                }
                //Check that the Meeting Candidate doesn't overlap an exclusion by
                //starting before it and ending in the middle.
                if (mc.getEnd().getTime().after(ex.getStart().getTime()) && mc.getEnd().getTime().before(ex.getEnd().getTime())) {
                    mc.setValidity(false);
                }
            }
        }
    }

    public void candidatesAreWorkingDays(ArrayList<MeetingCandidate> mcList) {
        //Check is a working day
        for (MeetingCandidate mc : mcList) {
            String day = this.getMeetingDayOfWeek(mc);
            String workingDays[] = theConfig.getDefaultWorkingWeekDays().split(" ");
            boolean isDayOfWorkingWeek = false;

            for (int i = 0; !isDayOfWorkingWeek && i < workingDays.length; i++) {
                if (day.equals(workingDays[i])) {
                    isDayOfWorkingWeek = true;
                    break;
                }
            }

            if (!isDayOfWorkingWeek) {
                mc.setValidity(false);
                continue;
            }

            //Set start to be correct time on this day.
            Calendar start = (Calendar) mc.getStart().clone();
            start.set(Calendar.HOUR_OF_DAY, theConfig.getDefaultWorkingDayStartHour());
            start.set(Calendar.MINUTE, 0);
            start.set(Calendar.SECOND, 0);

            Calendar end = (Calendar) start.clone();
            end.set(Calendar.HOUR_OF_DAY, theConfig.getDefaultWorkingDayEndHour());

            //Our method is printed many times but we only want this info once...
            if (!this.workingDayPrinted) {
                Debug.print("\n===Working Day -> " + start.getTime() + " To " + end.getTime() + "===");
                this.workingDayPrinted = true;
            }

            //At this point we know the candidate is a working day so just check to see if it falls between
            //the correct hours
            if (mc.getStart().getTime().compareTo(start.getTime()) >= 0) {
                if (mc.getEnd().getTime().compareTo(end.getTime()) <= 0) {
                    continue;
                }
            }

            mc.setValidity(false);
        }
    }

    public String getMeetingDayOfWeek(MeetingCandidate mc) {
        String dayString;
        int dayInt = mc.getStart().get(Calendar.DAY_OF_WEEK);

        switch (dayInt) {
            case Calendar.MONDAY:
                dayString = "monday";
                break;
            case Calendar.TUESDAY:
                dayString = "tuesday";
                break;
            case Calendar.WEDNESDAY:
                dayString = "wednesday";
                break;
            case Calendar.THURSDAY:
                dayString = "thursday";
                break;
            case Calendar.FRIDAY:
                dayString = "friday";
                break;
            case Calendar.SATURDAY:
                dayString = "saturday";
                break;
            case Calendar.SUNDAY:
                dayString = "sunday";
                break;
            default:
                dayString = "error";
        }
        return dayString;
    }
}
